﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Sharp3D.Math.Core;
using GLWrap;
using System.Runtime.InteropServices;
using System.Drawing;
using Tao.Platform.Windows;

namespace ReAnimation.Graphics.Handlers
{
    //TODO: сейчас есть зависимость скорости перемещения по сцене от FPS
    public class NavigationHandler : BaseHandler, IMessageFilter
    {
        private IDictionary<WindowsMessage, WindowsMessageHandler> _messageHandlers;
        private IDictionary<KeyboardKey, KeyEventHandler> _keyEventHandlers;
        private const int MOUSE_INQUIRE_INTERVAL = 100;//milliseconcs
        private Timer _timer;
        private float _moveCoeff = 0.01f;
        private float _rotationAngle = (float)Math.PI / 96 / 2;
        private bool _firstTimerAction;

        public NavigationHandler(IMoleculeDrawer moleculeDrawer)
            : base(moleculeDrawer)
        {
            _messageHandlers = new Dictionary<WindowsMessage, WindowsMessageHandler>();
            _keyEventHandlers = new Dictionary<KeyboardKey, KeyEventHandler>();

            _timer = new Timer();
            _timer.Interval = MOUSE_INQUIRE_INTERVAL;
            _timer.Tick += OnMouseInquireTimerTick;

            _messageHandlers[WindowsMessage.WM_KEYDOWN] = KeyDownHandler;
            _messageHandlers[WindowsMessage.WM_KEYUP] = KeyUpHandler;

            PrepareMoveHandlers();
        }

        private void OnMouseInquireTimerTick(object sender, EventArgs e)
        {
            
            SimpleOpenGlControl glControl = MoleculeDrawer.GlSceneManager.OpenGlControl;
            Point glControlCenter = new Point(glControl.Width /2, glControl.Height /2);
            Point glControlScreenCenter = MoleculeDrawer.GlSceneManager.OpenGlControl.PointToScreen(glControlCenter);

            int dx = Cursor.Position.X - glControlScreenCenter.X;
            int dy = Cursor.Position.Y - glControlScreenCenter.Y;

            if (!_firstTimerAction)
            {
                if (dx != 0)
                    RotateInDirection(_rotationAngle * dx / 6, Direction.Right);

                if (dy != 0)
                    RotateInDirection(_rotationAngle * dy / 6, Direction.Down);
            }

            Cursor.Position = glControlScreenCenter;
            Cursor.Show();

            MoleculeDrawer.GlSceneManager.Refresh();
            _firstTimerAction = false;
        }

        private void PrepareMoveHandlers()
        {
            InitMoveHandlers();
            InitRotationHandlers();

            _keyEventHandlers[KeyboardKey.VK_ESCAPE] = () =>
            {
                StopHandle();
            };
        }

        private void InitRotationHandlers()
        {
            _keyEventHandlers[KeyboardKey.W] = () =>
            {
                RotateInDirection(_rotationAngle, Direction.Up);
            };
            _keyEventHandlers[KeyboardKey.S] = () =>
            {
                RotateInDirection(_rotationAngle, Direction.Down);
            };
            _keyEventHandlers[KeyboardKey.A] = () =>
            {
                RotateInDirection(_rotationAngle, Direction.Left);
            };
            _keyEventHandlers[KeyboardKey.D] = () =>
            {
                RotateInDirection(_rotationAngle, Direction.Right);                
            };
        }

        private void RotateInDirection(float rotationAngle,Direction direction)
        {
            Vector3F upDownRotateVector = GetUpDownRotateVector();
            Vector3F leftRightRotateVector = GetLeftRightRotateVector();
            switch (direction)
            {
                case Direction.Down:
                    MoleculeDrawer.GlSceneManager.Camera.Rotate(-rotationAngle, upDownRotateVector);
                    break;
                case Direction.Up:
                    MoleculeDrawer.GlSceneManager.Camera.Rotate(rotationAngle, upDownRotateVector);
                    break;
                case Direction.Left:
                    MoleculeDrawer.GlSceneManager.Camera.Rotate(rotationAngle, leftRightRotateVector);
                    break;
                case Direction.Right:
                    MoleculeDrawer.GlSceneManager.Camera.Rotate(-rotationAngle, leftRightRotateVector);
                    break;
            }
        }

        private Vector3F GetLeftRightRotateVector()
        {
            Vector3F rotateVector = MoleculeDrawer.GlSceneManager.Camera.Up;
            return rotateVector;
        }

        private Vector3F GetUpDownRotateVector()
        {
            Vector3F rotateVector = Vector3F.CrossProduct(
                MoleculeDrawer.GlSceneManager.Camera.View,
                MoleculeDrawer.GlSceneManager.Camera.Up);
            rotateVector.Normalize();
            return rotateVector;
        }

        private void InitMoveHandlers()
        {
            _keyEventHandlers[KeyboardKey.VK_UP] = () =>
            {
                MoleculeDrawer.GlSceneManager.Camera.Move(Direction.Up, _moveCoeff);
            };
            _keyEventHandlers[KeyboardKey.VK_LEFT] = () =>
            {
                MoleculeDrawer.GlSceneManager.Camera.Move(Direction.Left, _moveCoeff);
            };
            _keyEventHandlers[KeyboardKey.VK_DOWN] = () =>
            {
                MoleculeDrawer.GlSceneManager.Camera.Move(Direction.Down, _moveCoeff);
            };
            _keyEventHandlers[KeyboardKey.VK_RIGHT] = () =>
            {
                MoleculeDrawer.GlSceneManager.Camera.Move(Direction.Right, _moveCoeff);
            };
        }

        protected override void PhysicallyStartHandle()
        {
            Application.AddMessageFilter(this);
            //TODO: need to eliminate flag
            _firstTimerAction = true;
           // _timer.Start();
        }

        protected override void PhysicallyStopHandle()
        {
            Application.RemoveMessageFilter(this);
           // _timer.Stop();
        }

        #region IMessageFilter Members

        private delegate bool WindowsMessageHandler(ref Message m);
        private delegate void KeyEventHandler();

        public bool PreFilterMessage(ref Message m)
        {
            if (!Enum.IsDefined(typeof(WindowsMessage), m.Msg))
                return false;

            WindowsMessage messageType = (WindowsMessage)m.Msg;

            if (_messageHandlers.ContainsKey(messageType))
                return _messageHandlers[messageType].Invoke(ref m);

            return false;
        }

        #endregion

        private bool KeyDownHandler(ref Message m)
        {
            int keyCode = (Int32)m.WParam;

            if (!Enum.IsDefined(typeof(KeyboardKey), keyCode)) 
                return false;

            KeyboardKey key = (KeyboardKey)keyCode;

            if (!_keyEventHandlers.ContainsKey(key)) 
                return false;

            _keyEventHandlers[key].Invoke();

            MoleculeDrawer.GlSceneManager.Refresh();
            return false;
        }

        private bool KeyUpHandler(ref Message m)
        {
            return false;
        }
    }
}
